package cn.com.zd.test.io;

import cn.com.zd.io.util.DataGeneratorUtils;
import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;

public class CreateTestData {

    private final long DATA_COUNT_10KW = 100000000L;
    private final long DATA_COUNT_5KW = 50000000L;
    private final long DATA_COUNT_2KW = 20000000L;

    private final String CREATE_FILE_PATH_10KW = "G:/a10kw.csv";
    private final String CREATE_FILE_PATH_5KW = "G:/a5kw.csv";
    private final String CREATE_FILE_PATH_2KW = "G:/a2kw.csv";


    /**
     * 创建测试数据
     *
     * @throws IOException
     */
    @Test
    public void creaeteCSVAsync() throws IOException {
        creaeteCSVAsync(DATA_COUNT_10KW, CREATE_FILE_PATH_10KW);
        creaeteCSVAsync(DATA_COUNT_5KW, CREATE_FILE_PATH_5KW);
        creaeteCSVAsync(DATA_COUNT_2KW, CREATE_FILE_PATH_2KW);
    }


    /**
     * 数据读取测试
     * 通过InputStream的方式
     *
     * @throws IOException
     */
    @Test
    public void readCSV2MenByInputStream() throws IOException {
        readCSV2MenByInputStream(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        readCSV2MenByInputStream(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        readCSV2MenByInputStream(CREATE_FILE_PATH_2KW);
    }

    /**
     * 数据读取测试
     * 通过Channel
     *
     * @throws IOException
     */
    @Test
    public void readCSV2MenByChannel() throws IOException {
        readCSV2MenByChannel(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        readCSV2MenByChannel(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        readCSV2MenByChannel(CREATE_FILE_PATH_2KW);
    }

    /**
     * 输入+输出 流的测试
     *
     * @throws IOException
     */
    @Test
    public void transCSVOnStream() throws IOException {
        transCSVOnStream(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        transCSVOnStream(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        transCSVOnStream(CREATE_FILE_PATH_2KW);
    }
    /**
     * 输入+输出 Channel的测试
     *
     * @throws IOException
     */
    @Test
    public void transCSVOnChannel() throws IOException {
        transCSVOnChannel(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        transCSVOnChannel(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        transCSVOnChannel(CREATE_FILE_PATH_2KW);
    }
    /**
     * 输入+输出 流的测试 按行处理
     *
     * @throws IOException
     */
    @Test
    public void transCSVOnStreamReadLine() throws IOException {
        transCSVOnStreamReadLine(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        transCSVOnStreamReadLine(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        transCSVOnStreamReadLine(CREATE_FILE_PATH_2KW);
    }

    @Test
    public void transCSVOnStreamReadLineNumberReader() throws IOException {
        transCSVOnStreamReadLineNumberReader(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        transCSVOnStreamReadLineNumberReader(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        transCSVOnStreamReadLineNumberReader(CREATE_FILE_PATH_2KW);
    }



    /**
     * 输入+输出 流的测试
     *
     * @throws IOException
     */
    @Test
    public void transCSVOnChannelReadLine() throws IOException {
        transCSVOnChannelReadLine(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        transCSVOnChannelReadLine(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        transCSVOnChannelReadLine(CREATE_FILE_PATH_2KW);
    }


    /**
     * 读取总行数 LineNumberReader
     */
    @Test
    public void readLineNumByStreamm() {
        readLineNumByStreamm(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        readLineNumByStreamm(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        readLineNumByStreamm(CREATE_FILE_PATH_2KW);
    }
    /**
     * 读取总行数 Channel
     */
    @Test
    public void readLineNumByChannel() throws IOException {
        readLineNumByChannel(CREATE_FILE_PATH_10KW);
        System.out.println("--------------------");
        readLineNumByChannel(CREATE_FILE_PATH_5KW);
        System.out.println("--------------------");
        readLineNumByChannel(CREATE_FILE_PATH_2KW);
    }

    /**
     * 创建测试数据
     *
     * @param count
     * @param outFilePath
     * @throws IOException
     */
    public void creaeteCSVAsync(long count, String outFilePath) throws IOException {
        long startDt = System.currentTimeMillis();
        File out = new File(outFilePath);
        if (out.exists()) {
            out.delete();
        }
        out.createNewFile();

        try (FileChannel fileOut = FileChannel.open(out.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE)) {
            for (long i = 0; i < count; i++) {
                ByteBuffer dataLine = ByteBuffer.wrap(getRandomLineData());
                fileOut.write(dataLine);
                if (i != 0 && i % 100000 == 0) {
                    System.out.println("已完成" + DataGeneratorUtils.formate(i) + "耗时" + DataGeneratorUtils.formate(System.currentTimeMillis() - startDt));
                }
            }
        }
        String times = DataGeneratorUtils.formate(System.currentTimeMillis() - startDt);
        System.out.println("共计：" + count + "条，耗时" + times);
    }

    /**
     * 输入+输出 流的实现
     *
     * @throws IOException
     */
    public void transCSVOnStream(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        File in = new File(filePath);
        System.out.println("文件大小：" + in.length());
        String COPY_2_FILE_PATH = filePath + System.currentTimeMillis() + ".csv";
        File out = new File(COPY_2_FILE_PATH);
        if (out.exists()) {
            out.delete();
        }
        out.createNewFile();

        byte[] b = new byte[1024 * 1024 * 5];
        try (FileInputStream ins = new FileInputStream(in);
             FileOutputStream outs = new FileOutputStream(out)) {
            int length = ins.read(b);
            while (length != -1) {
                outs.write(b, 0, b.length);
                length = ins.read(b);
            }
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("InputStream->OutputStream一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (in.length() * 1000.0000 / 1024 / 1024 / times) + " MB/s，目标文件在：" + COPY_2_FILE_PATH);
    }

    /**
     * 输入+输出 流的实现 bufferedreader 按行读
     *
     * @throws IOException
     */
    public void transCSVOnStreamReadLine(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        File in = new File(filePath);
        System.out.println("文件大小：" + in.length());
        String COPY_2_FILE_PATH = filePath + System.currentTimeMillis() + ".csv";
        File out = new File(COPY_2_FILE_PATH);
        if (out.exists()) {
            out.delete();
        }
        out.createNewFile();

        byte[] b = new byte[1024 * 1024 * 5];
        try (FileReader ins = new FileReader(in);
             FileOutputStream outs = new FileOutputStream(out);
             BufferedReader brins = new BufferedReader(ins)) {
            String line = brins.readLine();
            while (line != null) {
                outs.write(line.getBytes());
                outs.write(System.getProperty("line.separator").getBytes());
                outs.flush();
                line = brins.readLine();
            }
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("BufferedReader->OutputStream 按行处理一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (in.length() * 1000.0000 / 1024 / 1024 / times) + " MB/s，目标文件在：" + COPY_2_FILE_PATH);
    }

    /**
     * 输入+输出 流的实现 bufferedreader 按行读
     *
     * @throws IOException
     */
    public void transCSVOnStreamReadLineNumberReader(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        File in = new File(filePath);
        System.out.println("文件大小：" + in.length());
        String COPY_2_FILE_PATH = filePath + System.currentTimeMillis() + ".csv";
        File out = new File(COPY_2_FILE_PATH);
        if (out.exists()) {
            out.delete();
        }
        out.createNewFile();

        try (FileReader ins = new FileReader(in);
             LineNumberReader lineNumberReader = new LineNumberReader(ins);
             FileOutputStream outs = new FileOutputStream(out);) {
            String line = lineNumberReader.readLine();
            while (line != null) {
                outs.write(line.getBytes());
                outs.write(System.getProperty("line.separator").getBytes());
                outs.flush();
                line = lineNumberReader.readLine();
            }
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("LineNumberReader->OutputStream 按行处理一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (in.length() * 1000.0000 / 1024 / 1024 / times) + " MB/s，目标文件在：" + COPY_2_FILE_PATH);
    }

    /**
     * 输入+输出 Channel的实现
     *
     * @throws IOException
     */
    public void transCSVOnChannel(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        RandomAccessFile in = new RandomAccessFile(filePath, "r");
        long length = in.length();
        System.out.println("文件大小：" + length);
        FileChannel inChannel = in.getChannel();
        String COPY_2_FILE_PATH = filePath + System.currentTimeMillis() + ".csv";
        RandomAccessFile out = new RandomAccessFile(COPY_2_FILE_PATH, "rw");
        FileChannel outChannel = out.getChannel();
        long postition = 0l;

        while (postition < length) {
            long count = inChannel.transferTo(postition, length-postition, outChannel);
            postition += count;
        }
        outChannel.close();
        out.close();
        inChannel.close();
        in.close();
        long times = System.currentTimeMillis() - startDt;
        System.out.println("Channel一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (length * 1000.0000 / 1024 / 1024 / times) + " MB/s，目标文件在：" + COPY_2_FILE_PATH);
    }

    /**
     * 输入+输出 Channel的实现 按行处理
     *
     * @throws IOException
     */
    public void transCSVOnChannelReadLine(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        RandomAccessFile in = new RandomAccessFile(filePath, "r");
        long length = in.length();
        System.out.println("文件大小：" + length);
        FileChannel inChannel = in.getChannel();
        String COPY_2_FILE_PATH = filePath + System.currentTimeMillis() + ".csv";
        RandomAccessFile out = new RandomAccessFile(COPY_2_FILE_PATH, "rw");
        FileChannel outChannel = out.getChannel();

        // 开始按行读取
        int bufferSize = 1024 * 1024 * 5;  // 每一块的大小
        ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
        byte[] linedata = new byte[bufferSize];
        int linedataend = 0;
        while (inChannel.read(buffer) > 0) {
            buffer.flip();
            for (int i = 0; i < buffer.limit(); i++) {
                byte b = buffer.get();
                if (b == 10) {  // 如果遇到换行
                    String s = new String(linedata, 0, linedataend);
                    outChannel.write(ByteBuffer.wrap(linedata, 0, linedataend));
                    outChannel.write(ByteBuffer.wrap(System.getProperty("line.separator").getBytes()));
                    linedataend = 0;
                } else if (b == 13) {
                    continue;
                } else {
                    linedata[linedataend] = b;
                    linedataend++;
                }

            }
            buffer.clear(); // 清空buffer
        }
        if(linedataend > 0) {
            String s = new String(linedata, 0, linedataend);
            outChannel.write(ByteBuffer.wrap(linedata, 0, linedataend));
            outChannel.write(ByteBuffer.wrap(System.getProperty("line.separator").getBytes()));
        }
        outChannel.close();
        out.close();
        inChannel.close();
        in.close();
        long times = System.currentTimeMillis() - startDt;
        System.out.println("Channel 按行处理一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (length * 1000.0000 / 1024 / 1024 / times) + " MB/s，目标文件在：" + COPY_2_FILE_PATH);
    }

    /**
     * 数据读取测试，stream方式
     *
     * @param filePath
     * @throws IOException
     */

    public void readCSV2MenByInputStream(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        File in = new File(filePath);
        // Queue data = new LinkedBlockingDeque();

        byte[] b = new byte[1024 * 1024 * 5];
        try (FileInputStream ins = new FileInputStream(in);) {
            int length = ins.read(b);
            while (length != -1) {
//                data.add(new String(b,0,length));
                String s = new String(b, 0, length);
                length = ins.read(b);
            }
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("InputStream读取一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (in.length() * 1000.0000 / 1024 / 1024 / times) + " MB/s，源文件在：" + filePath);
    }

    /**
     * 数据读取测试，Channel方式（nio）
     *
     * @throws IOException
     */
    public void readCSV2MenByChannel(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        RandomAccessFile in = new RandomAccessFile(filePath, "r");

//        Queue data = new LinkedBlockingDeque();
        FileChannel channel = in.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024 * 5);
        int length = channel.read(buffer);
        while (length != -1) {
            buffer.flip();
//            data.add(buffer);
            String s = new String(buffer.array());
//            System.out.println(new String(buffer.array()));
            buffer.clear();
            length = channel.read(buffer);
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("FileChannel读取一共耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，速度：" + (in.length() * 1000.0000 / 1024 / 1024 / times) + " MB/s，源文件在：" + filePath);
    }

    /**
     * 读取总行数 流的方式 LineNumberReader
     *
     * @param filePath
     */
    public void readLineNumByStreamm(String filePath) {
        long startDt = System.currentTimeMillis();
        int total = 0;
        try (FileReader fileReader = new FileReader(filePath);
             LineNumberReader lr = new LineNumberReader(fileReader);) {
            while(lr.readLine() != null){
                total++;
            }
        } catch (Exception e) {
            // do something
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("LineNumberReader读取总行数" + total + " 耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，源文件在：" + filePath);
    }

    /**
     * 读取总行数 Channel的方式
     *
     * @param filePath
     */
    public void readLineNumByChannel(String filePath) throws IOException {
        long startDt = System.currentTimeMillis();
        long total = 0;
        try (RandomAccessFile file = new RandomAccessFile(filePath, "r");
             FileChannel channel = file.getChannel()) {
            long fileSize = channel.size();
            ByteBuffer buf = ByteBuffer.allocateDirect((int) fileSize);
            while (channel.read(buf) > 0) {
                buf.flip();
                while (buf.hasRemaining()) {
                    byte b = buf.get();
                    if (b == '\n') {
                        total++;
                    }
                }
                buf.clear();
            }
        }
        long times = System.currentTimeMillis() - startDt;
        System.out.println("readLineNumByChannel" + total + " 耗时：" + DataGeneratorUtils.formate(times) + " 毫秒，源文件在：" + filePath);

    }

    /**
     * 产生随机数据行
     *
     * @return
     */
    public byte[] getRandomLineData() {
        List<String> data = new ArrayList<>();
        data.add(DataGeneratorUtils.getRandomName());
        data.add(DataGeneratorUtils.getRandomSex() + "");
        data.add(DataGeneratorUtils.generateIdCard());
        data.add(new SimpleDateFormat("yyyy-HH-dd hh:MM:ss").format(new Date()));
        data.add(DataGeneratorUtils.generateRandomString(5));
        data.add(DataGeneratorUtils.generateRandomMobile());
        data.add(DataGeneratorUtils.generateRandomPhoneNumber());
        String country = DataGeneratorUtils.generateRandomCountry();
        data.add(country);
        data.add(DataGeneratorUtils.getCountryCode(country));
        data.add(DataGeneratorUtils.generateRandomCity());
        StringBuffer sb = new StringBuffer();
        for (String datum : data) {
            sb.append(",")
                    .append(datum);
        }
        sb.append(System.getProperty("line.separator"));
        return sb.toString().substring(1).getBytes();
    }


}
